#include "registerview.hpp"
#include "ui_registerview.h"
#include <QDebug>

RegisterView::RegisterView(QWidget *parent) :
    QDockWidget(parent),
    ui(new Ui::RegisterView)
{
    ui->setupUi(this);

    this->initializeView();
    connect(ui->cb_trisa_0, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisaStateChanged(int)));
    connect(ui->cb_trisa_1, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisaStateChanged(int)));
    connect(ui->cb_trisa_2, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisaStateChanged(int)));
    connect(ui->cb_trisa_3, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisaStateChanged(int)));
    connect(ui->cb_trisa_4, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisaStateChanged(int)));

    connect(ui->cb_trisb_0, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisbStateChanged(int)));
    connect(ui->cb_trisb_1, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisbStateChanged(int)));
    connect(ui->cb_trisb_2, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisbStateChanged(int)));
    connect(ui->cb_trisb_3, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisbStateChanged(int)));
    connect(ui->cb_trisb_4, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisbStateChanged(int)));
    connect(ui->cb_trisb_5, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisbStateChanged(int)));
    connect(ui->cb_trisb_6, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisbStateChanged(int)));
    connect(ui->cb_trisb_7, SIGNAL(stateChanged(int)), this, SLOT(onCbTrisbStateChanged(int)));

    connect(ui->cb_porta_0, SIGNAL(stateChanged(int)), this, SLOT(onCbPortaStateChanged(int)));
    connect(ui->cb_porta_1, SIGNAL(stateChanged(int)), this, SLOT(onCbPortaStateChanged(int)));
    connect(ui->cb_porta_2, SIGNAL(stateChanged(int)), this, SLOT(onCbPortaStateChanged(int)));
    connect(ui->cb_porta_3, SIGNAL(stateChanged(int)), this, SLOT(onCbPortaStateChanged(int)));
    connect(ui->cb_porta_4, SIGNAL(stateChanged(int)), this, SLOT(onCbPortaStateChanged(int)));

    connect(ui->cb_portb_0, SIGNAL(stateChanged(int)), this, SLOT(onCbPortbStateChanged(int)));
    connect(ui->cb_portb_1, SIGNAL(stateChanged(int)), this, SLOT(onCbPortbStateChanged(int)));
    connect(ui->cb_portb_2, SIGNAL(stateChanged(int)), this, SLOT(onCbPortbStateChanged(int)));
    connect(ui->cb_portb_3, SIGNAL(stateChanged(int)), this, SLOT(onCbPortbStateChanged(int)));
    connect(ui->cb_portb_4, SIGNAL(stateChanged(int)), this, SLOT(onCbPortbStateChanged(int)));
    connect(ui->cb_portb_5, SIGNAL(stateChanged(int)), this, SLOT(onCbPortbStateChanged(int)));
    connect(ui->cb_portb_6, SIGNAL(stateChanged(int)), this, SLOT(onCbPortbStateChanged(int)));
    connect(ui->cb_portb_7, SIGNAL(stateChanged(int)), this, SLOT(onCbPortbStateChanged(int)));

    this->highlightingState = true;
}

RegisterView::~RegisterView()
{
    disconnect(ui->tbl_bank0, SIGNAL(cellChanged(int,int)), this, SLOT(on_tbl_bank0_cellChanged(int,int)));
    disconnect(ui->tbl_bank1, SIGNAL(cellChanged(int,int)), this, SLOT(on_tbl_bank1_cellChanged(int,int)));

    QList<int> values = this->itemsBank0.keys();
    while (!values.isEmpty())
	delete this->itemsBank0.take(values.takeFirst());

    values = this->itemsBank1.keys();
    while (!values.isEmpty())
	delete this->itemsBank1.take(values.takeFirst());

    delete ui;
}

void RegisterView::changeEvent(QEvent *e)
{
    QDockWidget::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}


void RegisterView::setTimer(QTableWidgetItem *item)
{
    if (!this->highlightingState)
	return;

    if (!this->changedTimerMap.contains(item))
    {
	item->setBackgroundColor(QColor(255, 0, 0));
	QTimer *newTimer = new QTimer(this);
	newTimer->setInterval(5000);
	newTimer->setSingleShot(true);
	newTimer->setProperty("item", (int) item);
	this->changedTimerMap.insert(item, newTimer);
	connect(newTimer, SIGNAL(timeout()), this, SLOT(onChangedTimerExpires()));
	newTimer->start();
    } else {
	QTimer *oldTimer = this->changedTimerMap.find(item).value();
	oldTimer->stop();
	oldTimer->setInterval(5000);
	oldTimer->start();
    }
}


void RegisterView::onRegisterChanged(Register *reg)
{
    // Only Bank 0
    if(reg->getBank() == 0)
    {
	QTableWidgetItem* item = itemsBank0.find(reg->getAddress()).value();

	item->setText(reg->toString());

	this->setTimer(item);

	// Port A - Register
	if(reg->getAddress() == 5)
	{
	    ui->cb_porta_0->setChecked(reg->isSet(0));
	    ui->cb_porta_1->setChecked(reg->isSet(1));
	    ui->cb_porta_2->setChecked(reg->isSet(2));
	    ui->cb_porta_3->setChecked(reg->isSet(3));
	    ui->cb_porta_4->setChecked(reg->isSet(4));

	// Port B - Register
	} else if(reg->getAddress() == 6) {
	    ui->cb_portb_0->setChecked(reg->isSet(0));
	    ui->cb_portb_1->setChecked(reg->isSet(1));
	    ui->cb_portb_2->setChecked(reg->isSet(2));
	    ui->cb_portb_3->setChecked(reg->isSet(3));
	    ui->cb_portb_4->setChecked(reg->isSet(4));
	    ui->cb_portb_5->setChecked(reg->isSet(5));
	    ui->cb_portb_6->setChecked(reg->isSet(6));
	    ui->cb_portb_7->setChecked(reg->isSet(7));
	}

    // Only Bank 1
    } else if(reg->getBank() == 1) {
	QTableWidgetItem* item = itemsBank1.find(reg->getAddress()).value();

	item->setText(reg->toString());

	this->setTimer(item);

	// Tris A - Register
	if(reg->getAddress() == 5)
	{
	    ui->cb_trisa_0->setChecked(reg->isSet(0));
	    ui->cb_trisa_1->setChecked(reg->isSet(1));
	    ui->cb_trisa_2->setChecked(reg->isSet(2));
	    ui->cb_trisa_3->setChecked(reg->isSet(3));
	    ui->cb_trisa_4->setChecked(reg->isSet(4));

	// Tris B - Register
	} else if(reg->getAddress() == 6) {
	    ui->cb_trisb_0->setChecked(reg->isSet(0));
	    ui->cb_trisb_1->setChecked(reg->isSet(1));
	    ui->cb_trisb_2->setChecked(reg->isSet(2));
	    ui->cb_trisb_3->setChecked(reg->isSet(3));
	    ui->cb_trisb_4->setChecked(reg->isSet(4));
	    ui->cb_trisb_5->setChecked(reg->isSet(5));
	    ui->cb_trisb_6->setChecked(reg->isSet(6));
	    ui->cb_trisb_7->setChecked(reg->isSet(7));
	}

    // Bank 0 & 1
    } else if (reg->getBank() == 2) {
	QTableWidgetItem* item1 = itemsBank0.find(reg->getAddress()).value();
	QTableWidgetItem* item2 = itemsBank1.find(reg->getAddress()).value();

	item1->setText(reg->toString());
	item2->setText(reg->toString());

	this->setTimer(item1);
	this->setTimer(item2);

	// Status - Register
	if(reg->getAddress() == 3)
	{
	    ui->cb_status_0->setChecked(reg->isSet(0));
	    ui->cb_status_1->setChecked(reg->isSet(1));
	    ui->cb_status_2->setChecked(reg->isSet(2));
	    ui->cb_status_3->setChecked(reg->isSet(3));
	    ui->cb_status_4->setChecked(reg->isSet(4));
	    ui->cb_status_5->setChecked(reg->isSet(5));

	// Update FSR
	} else if (reg->getAddress() == 4)
		ui->ln_fsr->setText(reg->toString());

    // W - Register
    } else if (reg->getAddress() == -1 && reg->getBank() == -1){
	ui->ln_working_register->setText(reg->toString());
	return;

    // Dont know what to do ..
    } else {
	qWarning() << "Unknown Register changed: " << reg->getAddress();
	return;
    }
}


void RegisterView::initializeView()
{
    this->initializing = true;

    int i = 0;

    //Indirect addr.(1) 0x00
    QTableWidgetItem *indirectAddress = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, indirectAddress);
    this->itemsBank0.insert(i, indirectAddress);
    indirectAddress = new QTableWidgetItem("00");
    ui->tbl_bank1->setItem(0, i, indirectAddress);
    this->itemsBank1.insert(i++, indirectAddress);

    //TMR0/OPTION_REG 0x01
    QTableWidgetItem *option = new QTableWidgetItem("00");
    QTableWidgetItem *tmr0 = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, option);
    ui->tbl_bank1->setItem(0, i, tmr0);
    this->itemsBank0.insert(i, option);
    this->itemsBank1.insert(i++, tmr0);

    //PCL 0x02
    QTableWidgetItem *pcl = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, pcl);
    this->itemsBank0.insert(i, pcl);
    pcl = new QTableWidgetItem("00");
    ui->tbl_bank1->setItem(0, i, pcl);
    this->itemsBank1.insert(i++, pcl);

    //STATUS 0x03
    QTableWidgetItem *status = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, status);
    this->itemsBank0.insert(i, status);
    status = new QTableWidgetItem("00");
    ui->tbl_bank1->setItem(0, i, status);
    this->itemsBank1.insert(i++, status);

    //FSR 0x04
    QTableWidgetItem *fsr = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, fsr);
    this->itemsBank0.insert(i, fsr);
    fsr = new QTableWidgetItem("00");
    ui->tbl_bank1->setItem(0, i, fsr);
    this->itemsBank1.insert(i++, fsr);

    //PORTA/TRISA 0x05
    QTableWidgetItem *portA = new QTableWidgetItem("00");
    QTableWidgetItem *trisA = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, portA);
    ui->tbl_bank1->setItem(0, i, trisA);
    this->itemsBank0.insert(i, portA);
    this->itemsBank1.insert(i++, trisA);

    //PORTB/TRISB 0x06
    QTableWidgetItem *portB = new QTableWidgetItem("00");
    QTableWidgetItem *trisB = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, portB);
    ui->tbl_bank1->setItem(0, i, trisB);
    this->itemsBank0.insert(i, portB);
    this->itemsBank1.insert(i++, trisB);

    //Not used 0x07
    QTableWidgetItem *na = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, na);
    this->itemsBank0.insert(i, na);
    na = new QTableWidgetItem("00");
    ui->tbl_bank1->setItem(0, i, na);
    this->itemsBank1.insert(i++, na);

    //EEDATA/EECON1 0x08
    QTableWidgetItem *eedata = new QTableWidgetItem("00");
    QTableWidgetItem *eecon1 = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, eedata);
    ui->tbl_bank1->setItem(0, i, eecon1);
    this->itemsBank0.insert(i, eedata);
    this->itemsBank1.insert(i++, eecon1);

    //EEADR/EECON2(1) 0x09
    QTableWidgetItem *eeadr = new QTableWidgetItem("00");
    QTableWidgetItem *eecon2 = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, eeadr);
    ui->tbl_bank1->setItem(0, i, eecon2);
    this->itemsBank0.insert(i, eeadr);
    this->itemsBank1.insert(i++, eecon2);

    //PCLATH 0x0A
    QTableWidgetItem *pclath = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, pclath);
    this->itemsBank0.insert(i, pclath);
    pclath = new QTableWidgetItem("00");
    ui->tbl_bank1->setItem(0, i, pclath);
    this->itemsBank1.insert(i++, pclath);

    //INTCON 0x0B
    QTableWidgetItem *intcon = new QTableWidgetItem("00");
    ui->tbl_bank0->setItem(0, i, intcon);
    this->itemsBank0.insert(i, intcon);
    intcon = new QTableWidgetItem("00");
    ui->tbl_bank1->setItem(0, i, intcon);
    this->itemsBank1.insert(i++, intcon);

    //Initiate "normal" registers start at  0x0C
    for(; i <= 0x4F; i++)
    {
	int row = i & 240;
	row = row >> 4;

	int col = i & 15;

	QTableWidgetItem *temp = new QTableWidgetItem("00");
	ui->tbl_bank0->setItem(row, col, temp);
	this->itemsBank0.insert(i, temp);
	temp = new QTableWidgetItem("00");
	ui->tbl_bank1->setItem(row, col, temp);
	this->itemsBank1.insert(i, temp);
    }

    this->initializing = false;
}


void RegisterView::on_tbl_bank0_cellChanged(int row, int column)
{
    if (ui->tbl_bank1->item(row, column) == NULL || this->initializing)
	return;

    int value = ui->tbl_bank0->item(row, column)->text().toInt(0, 16);

    int identifier = row << 4;
    identifier += column;

    emit requestChangeRegister(identifier, value);
}


void RegisterView::on_tbl_bank1_cellChanged(int row, int column)
{
    if (ui->tbl_bank1->item(row, column) == NULL || this->initializing)
	return;

    int identifier = row << 4;
    identifier += column;
    identifier = identifier | 128;

    int value = ui->tbl_bank1->item(row, column)->text().toInt(0, 16);
    emit requestChangeRegister(identifier, value);
}


void RegisterView::onBankChanged(bool bank)
{
    if (bank)
	ui->tabWidget->setCurrentIndex(1);
    else
	ui->tabWidget->setCurrentIndex(0);
}


void RegisterView::onProgramCounterChanged(int programCounter)
{
    QString pc = QString::number(programCounter, 16).toUpper();
    while(pc.size() < 4)
    {
	pc.prepend("0");
    }
    ui->ln_program_counter->setText(pc);

}


void RegisterView::onChangedTimerExpires()
{
    QTimer *timer = (QTimer*) QObject::sender();
    QTableWidgetItem *item = (QTableWidgetItem*) timer->property("item").toInt();
    item->setBackgroundColor(QColor(255, 255, 255));
    this->changedTimerMap.remove(item);
}


void RegisterView::onCbTrisaStateChanged(int state)
{
    ui->cb_porta_0->setEnabled(ui->cb_trisa_0->isChecked());
    ui->cb_porta_1->setEnabled(ui->cb_trisa_1->isChecked());
    ui->cb_porta_2->setEnabled(ui->cb_trisa_3->isChecked());
    ui->cb_porta_3->setEnabled(ui->cb_trisa_4->isChecked());
    ui->cb_porta_4->setEnabled(ui->cb_trisa_4->isChecked());

    int value = 0;
    value += ui->cb_trisa_4->isChecked() * pow(2, 4);
    value += ui->cb_trisa_3->isChecked() * pow(2, 3);
    value += ui->cb_trisa_2->isChecked() * pow(2, 2);
    value += ui->cb_trisa_1->isChecked() * 2;
    value += ui->cb_trisa_0->isChecked();
    emit requestChangeRegister(0x85, value);
}


void RegisterView::onCbTrisbStateChanged(int state)
{
    ui->cb_portb_0->setEnabled(ui->cb_trisb_0->isChecked());
    ui->cb_portb_1->setEnabled(ui->cb_trisb_1->isChecked());
    ui->cb_portb_2->setEnabled(ui->cb_trisb_2->isChecked());
    ui->cb_portb_3->setEnabled(ui->cb_trisb_3->isChecked());
    ui->cb_portb_4->setEnabled(ui->cb_trisb_4->isChecked());
    ui->cb_portb_5->setEnabled(ui->cb_trisb_5->isChecked());
    ui->cb_portb_6->setEnabled(ui->cb_trisb_6->isChecked());
    ui->cb_portb_7->setEnabled(ui->cb_trisb_7->isChecked());

    int value = 0;
    value += ui->cb_trisb_7->isChecked() * pow(2, 7);
    value += ui->cb_trisb_6->isChecked() * pow(2, 6);
    value += ui->cb_trisb_5->isChecked() * pow(2, 5);
    value += ui->cb_trisb_4->isChecked() * pow(2, 4);
    value += ui->cb_trisb_3->isChecked() * pow(2, 3);
    value += ui->cb_trisb_2->isChecked() * pow(2, 2);
    value += ui->cb_trisb_1->isChecked() * 2;
    value += ui->cb_trisb_0->isChecked();
    emit requestChangeRegister(0x86, value);
}


void RegisterView::onCbPortaStateChanged(int state)
{
    int value = 0;
    value += ui->cb_porta_4->isChecked() * pow(2, 4);
    value += ui->cb_porta_3->isChecked() * pow(2, 3);
    value += ui->cb_porta_2->isChecked() * pow(2, 2);
    value += ui->cb_porta_1->isChecked() * 2;
    value += ui->cb_porta_0->isChecked();
    emit requestChangeRegister(5, value);
}

void RegisterView::onCbPortbStateChanged(int state)
{
    int value = 0;
    value += ui->cb_portb_7->isChecked() * pow(2, 7);
    value += ui->cb_portb_6->isChecked() * pow(2, 6);
    value += ui->cb_portb_5->isChecked() * pow(2, 5);
    value += ui->cb_portb_4->isChecked() * pow(2, 4);
    value += ui->cb_portb_3->isChecked() * pow(2, 3);
    value += ui->cb_portb_2->isChecked() * pow(2, 2);
    value += ui->cb_portb_1->isChecked() * 2;
    value += ui->cb_portb_0->isChecked();
    emit requestChangeRegister(6, value);
}


void RegisterView::setHighlighting(bool state)
{
    this->highlightingState = state;
}


void RegisterView::on_cb_status_5_stateChanged(int state)
{
    emit requestChangeBank(state == Qt::Checked);
}
